#include "array_buffer.h"
#include <stdlib.h>
#include <string.h>

#define __min(a, b)	((a)>(b)?(b):(a))
#define __max(a, b)	((a)>(b)?(a):(b))

bool ArrayBuffer::Move(ArrayBuffer* dstBuffer, ArrayBuffer* srcBuffer)
{
	if (dstBuffer->PushFrom(srcBuffer)) {
		srcBuffer->PopAll();
		return(true);
	}
	else {
		return(false);
	}
}

uint32_t ArrayBuffer::MoveAsMuch(ArrayBuffer* dstBuffer, ArrayBuffer* srcBuffer)
{
	uint32_t moveCount = dstBuffer->PushAsMuch(srcBuffer);
	if (moveCount > 0) {
		srcBuffer->Pop(moveCount);
	}
	return(moveCount);
}

ArrayBuffer::ArrayBuffer(uint32_t typeSize, uint32_t initArrayLength, uint32_t maxArrayLength)
{
	m_typeSize = typeSize;
	m_capacity = initArrayLength * typeSize;
	m_maxCapacity = __max(m_capacity, maxArrayLength * typeSize);
	m_buffer = new uint8_t[m_capacity];
	m_iPeek = 0;
	m_dataSize = 0;
	m_lostCount = 0;
}

ArrayBuffer::~ArrayBuffer()
{
	delete[] m_buffer;
}

bool ArrayBuffer::_EnsurePushSpace(uint32_t pushSize)
{
	uint32_t iPush = m_iPeek + m_dataSize;
	// 尾段足够
	if (iPush + pushSize <= m_capacity) {
		return(true);
	}

	// 前后空余段合起来足够，将已有数据段前移
	uint32_t wishDataSize = m_dataSize + pushSize;
	if (wishDataSize <= m_capacity) {
		memmove(m_buffer, (uint8_t *)m_buffer + m_iPeek, m_dataSize);
		m_iPeek = 0;
		return(true);
	}

	// 动态分配更大空间，直至最大容量限制
	if (wishDataSize <= m_maxCapacity) {
		uint32_t biggerCapacity = __min(m_capacity * 2, m_maxCapacity);
		uint32_t newCapacity = __max(wishDataSize, biggerCapacity);
		uint8_t* newBuffer = new uint8_t[newCapacity];
		memcpy(newBuffer, (uint8_t*)m_buffer + m_iPeek, m_dataSize);
		delete[] m_buffer;
		m_buffer = newBuffer;
		m_capacity = newCapacity;
		m_iPeek = 0;
		return(true);
	}
	// 即使最大空间也不够用，但也用足
	else {
		if (m_capacity < m_maxCapacity) {
			uint8_t* maxBuffer = new uint8_t[m_maxCapacity];
			memcpy(maxBuffer, m_buffer, m_capacity);
			delete[] m_buffer;
			m_buffer = maxBuffer;
			m_capacity = m_maxCapacity;
		}
		return(false);
	}
}

void ArrayBuffer::Reset(void)
{
	m_iPeek = 0;
	m_dataSize = 0;
	m_lostCount = 0;
}

bool ArrayBuffer::PushInt(long iv)
{
	if (m_typeSize != sizeof(long)) {
		return(false);
	}

	if (_EnsurePushSpace(m_typeSize)) {
		uint32_t iPush = m_iPeek + m_dataSize;
		*((long*)(m_buffer + iPush)) = iv;
		m_dataSize += m_typeSize;
		return(true);
	}
	else {
		return(false);
	}
}

bool ArrayBuffer::PushFloat(double fv)
{
	if (m_typeSize != sizeof(double)) {
		return(false);
	}

	if (_EnsurePushSpace(m_typeSize)) {
		uint32_t iPush = m_iPeek + m_dataSize;
		*((double*)(m_buffer + iPush)) = fv;
		m_dataSize += m_typeSize;
		return(true);
	}
	else {
		return(false);
	}
}

bool ArrayBuffer::Push(const void* cache, uint32_t cacheCount)
{
	uint32_t cacheSize = cacheCount * m_typeSize;
	if (cacheCount > 0 && _EnsurePushSpace(cacheSize)) {
		uint32_t iPush = m_iPeek + m_dataSize;
		memcpy(m_buffer + iPush, cache, cacheSize);
		m_dataSize += cacheSize;
		return(true);
	}
	else {
		return(false);
	}
}

void ArrayBuffer::InjectInt(long iv)
{
	if (m_typeSize != sizeof(long)) {
		return;
	}

	if (!PushInt(iv)) {
		m_lostCount++;
		memmove(m_buffer, m_buffer + sizeof(long), m_capacity - sizeof(long));
		*((long*)(m_buffer + m_capacity - sizeof(long))) = iv;
		m_iPeek = 0;
		m_dataSize = m_capacity;
	}
}

void ArrayBuffer::InjectFloat(double fv)
{
	if (m_typeSize != sizeof(double)) {
		return;
	}

	if (!PushFloat(fv)) {
		m_lostCount++;
		memmove(m_buffer, m_buffer + sizeof(double), m_capacity - sizeof(double));
		*((double*)(m_buffer + m_capacity - sizeof(double))) = fv;
		m_iPeek = 0;
		m_dataSize = m_capacity;
	}
}

void ArrayBuffer::Inject(const void* cache, uint32_t cacheCount)
{
	if (!Push(cache, cacheCount)) {
		// 到这一步空间不够只能挤旧纳新，用满容量
		uint32_t cacheSize = cacheCount * m_typeSize;
		uint32_t remainSize = m_capacity - cacheSize;
		uint32_t iRemain = m_iPeek + m_dataSize - remainSize;
		m_lostCount = (m_dataSize - remainSize) / m_typeSize;
		memmove(m_buffer, m_buffer + iRemain, remainSize);
		memcpy(m_buffer + remainSize, cache, cacheSize);
		m_iPeek = 0;
		m_dataSize = m_capacity;
	}
}

bool ArrayBuffer::PushFrom(ArrayBuffer* pSrcBuffer)
{
	return(Push(pSrcBuffer->PeekReadArray(), pSrcBuffer->DataCount()));
}

uint32_t ArrayBuffer::PushAsMuch(ArrayBuffer* pSrcBuffer)
{
	uint32_t srcDataSize = pSrcBuffer->DataSize();
	if (srcDataSize == 0) {
		return(0);
	}

	// 尾段足够
	uint32_t iPush = m_iPeek + m_dataSize;
	if (iPush + srcDataSize <= m_capacity) {
		memcpy(m_buffer + iPush, pSrcBuffer->PeekReadArray(), srcDataSize);
		m_dataSize += srcDataSize;
		return(srcDataSize / m_typeSize);
	}

	// 数据段顶头，合并空余段
	if (m_iPeek > 0) {
		memmove(m_buffer, m_buffer + m_iPeek, m_dataSize);
		m_iPeek = 0;
	}
	// 最大利用空余段
	uint32_t freeSize = m_capacity - m_dataSize;
	uint32_t muchSize = __min(freeSize, srcDataSize);
	if (muchSize > 0) {
		memcpy(m_buffer + iPush, pSrcBuffer->PeekReadArray(), muchSize);
		m_dataSize += muchSize;
	}
	return(muchSize / m_typeSize);
}

void ArrayBuffer::PopAll(void)
{
	m_iPeek = 0;
	m_dataSize = 0;
}

void ArrayBuffer::Pop(uint32_t outCount)
{
	uint32_t outSize = outCount * m_typeSize;
	if (outSize >= m_dataSize) {
		PopAll();
	}
	else {
		m_iPeek += outSize;
		m_dataSize -= outSize;
	}
}

uint32_t ArrayBuffer::PopAsMuch(void* dataBuf, uint32_t maxCount)
{
	uint32_t muchCount = __min(m_dataSize / m_typeSize, maxCount);
	if (muchCount > 0) {
		memcpy(dataBuf, m_buffer + m_iPeek, muchCount * m_typeSize);
		Pop(muchCount);
		return(muchCount);
	}
	else {
		return(0);
	}
}

SyncBuffer::SyncBuffer(uint32_t typeSize, uint32_t initArrayLength, uint32_t maxArrayLength, int delay)
	: ArrayBuffer(typeSize, initArrayLength, maxArrayLength)
{
	m_delay = delay;
	m_delayCount = 0;
}

void SyncBuffer::Reset()
{
	ArrayBuffer::Reset();
	m_delayCount = 0;
}

void SyncBuffer::SetDelay(int delay)
{
	m_delay = delay;
}

bool SyncBuffer::PushInt(long iv)
{
	bool pushOk = true;
	if (m_delayCount < m_delay) {
		// 需要复制递增
		while (m_delayCount < m_delay && pushOk) {
			pushOk = ArrayBuffer::PushInt(iv);
			if (pushOk) {
				m_delayCount++;
			}
		}
		if (pushOk) {
			pushOk = ArrayBuffer::PushInt(iv);
		}
	}
	else if (m_delayCount > m_delay) {
		// 需要扔掉
		m_delayCount--;					// 每次只能扔掉一个
		pushOk = true;					// 假装成功
	}
	else {
		// 正常连续推入阶段
		pushOk = ArrayBuffer::PushInt(iv);
	}
	return(pushOk);
}

bool SyncBuffer::PushFloat(double fv)
{
	bool pushOk = true;
	if (m_delayCount < m_delay) {
		// 需要复制递增
		while (m_delayCount < m_delay && pushOk) {
			pushOk = ArrayBuffer::PushFloat(fv);
			if (pushOk) {
				m_delayCount++;
			}
		}
		if (pushOk) {
			pushOk = ArrayBuffer::PushFloat(fv);
		}
	}
	else if(m_delayCount > m_delay) {
		// 需要扔掉
		m_delayCount--;					// 每次只能扔掉一个
		pushOk = true;					// 假装成功
	}
	else {
		// 正常连续推入阶段
		pushOk = ArrayBuffer::PushFloat(fv);
	}
	return(pushOk);
}

bool SyncBuffer::Push(const void* cache, uint32_t cacheCount)
{
	//uint32_t cacheSize = cacheCount * m_typeSize;
	bool pushOk = true;
	if (m_delayCount < m_delay) {
		// 需要复制递增
		while (m_delayCount < m_delay && pushOk) {
			pushOk = ArrayBuffer::Push(cache, 1);
			if (pushOk) {
				m_delayCount++;
			}
		}
		if (pushOk) {
			pushOk = ArrayBuffer::Push(cache, cacheCount);
		}
	}
	// 需要扔掉
	else if (m_delayCount > m_delay) {
		uint32_t dropCount = m_delayCount - m_delay;
		// 需要扔掉
		if (cacheCount <= dropCount) {
			m_delayCount -= cacheCount;
			pushOk = true;				// 成功全部扔掉
		}
		// 部分扔掉，剩余的推入
		else {
			m_delayCount -= dropCount;
			pushOk = ArrayBuffer::Push((const uint8_t *)cache + dropCount * m_typeSize, cacheCount - dropCount);
		}
	}
	// 正常连续推入阶段
	else {
		pushOk = ArrayBuffer::Push(cache, cacheCount);
	}
	return(pushOk);
}

CxTransport::CxTransport(uint32_t typeSize, uint32_t initArrayLength, uint32_t maxArrayLength)
{
	m_transBuffer = new ArrayBuffer(typeSize, initArrayLength, maxArrayLength);
	m_cs = new CxSection();
}

CxTransport::~CxTransport()
{
	delete m_cs;
	delete m_transBuffer;
}

void CxTransport::Reset(void)
{
	m_cs->Enter();
	m_transBuffer->Reset();
	m_cs->Leave();
}

uint32_t CxTransport::LostCount(void)
{
	m_cs->Enter();
	uint32_t lostCount = m_transBuffer->LostCount();
	m_cs->Leave();
	return(lostCount);
}

void CxTransport::Inject(const void* cache, uint32_t cacheCount)
{
	m_cs->Enter();
	m_transBuffer->Inject(cache, cacheCount);
	m_cs->Leave();
}

uint32_t CxTransport::MoveFromAsMuch(ArrayBuffer* pSrcBuffer)
{
	m_cs->Enter();
	uint32_t moveCount = ArrayBuffer::MoveAsMuch(m_transBuffer, pSrcBuffer);
	m_cs->Leave();
	return(moveCount);
}

uint32_t CxTransport::MoveToAsMuch(ArrayBuffer* pDstBuffer)
{
	m_cs->Enter();
	uint32_t moveCount = ArrayBuffer::MoveAsMuch(pDstBuffer, m_transBuffer);
	m_cs->Leave();
	return(moveCount);
}

CxDispatcher::CxDispatcher(uint32_t typeSize, uint32_t initArrayLength, uint32_t maxArrayLength)
{
	m_typeSize = typeSize;
	m_initArrayLength = initArrayLength;
	m_maxArrayLength = maxArrayLength;
	m_transBufferList = new ObjList(8);
	m_cs = new CxSection();
}

CxDispatcher::~CxDispatcher()
{
	delete m_cs;
	delete m_transBufferList;
}

void CxDispatcher::Reset(void)
{
	m_cs->Enter();
	uint32_t n = m_transBufferList->Count();
	for (uint32_t i = 0; i < n; i++) {
		((ArrayBuffer*)m_transBufferList->GetItem(i))->Reset();
	}
	m_cs->Leave();
}

void CxDispatcher::Inject(const void* cache, uint32_t cacheSize)
{
	m_cs->Enter();
	uint32_t n = m_transBufferList->Count();
	for (uint32_t i = 0; i < n; i++) {
		((ArrayBuffer*)m_transBufferList->GetItem(i))->Inject(cache, cacheSize);
	}
	m_cs->Leave();
}

uint32_t CxDispatcher::TotalLostCount(void)
{
	uint32_t lostCount = 0;
	m_cs->Enter();
	uint32_t n = m_transBufferList->Count();
	for (uint32_t i = 0; i < n; i++) {
		lostCount += ((ArrayBuffer*)m_transBufferList->GetItem(i))->LostCount();
	}
	m_cs->Leave();
	return(lostCount);
}

uint32_t CxDispatcher::MoveInAsMuch(ArrayBuffer* pSrcBuffer)
{
	m_cs->Enter();
	uint32_t n = m_transBufferList->Count();
	uint32_t maxMoveSize = 0;
	for (uint32_t i = 0; i < n; i++) {
		uint32_t moveSize = ((ArrayBuffer*)m_transBufferList->GetItem(i))->PushAsMuch(pSrcBuffer);
		maxMoveSize = __max(maxMoveSize, moveSize);
	}
	if (maxMoveSize > 0) {
		pSrcBuffer->Pop(maxMoveSize);
	}
	m_cs->Leave();
	return(maxMoveSize);
}

void* CxDispatcher::Subscribe(void)
{
	ArrayBuffer* newArrayBuffer = new ArrayBuffer(m_typeSize, m_initArrayLength, m_maxArrayLength);
	m_cs->Enter();
	m_transBufferList->Attach(newArrayBuffer);
	m_cs->Leave();
	return(newArrayBuffer);
}

void CxDispatcher::Unsubscribe(void* h)
{
	m_cs->Enter();
	m_transBufferList->Detach(h);
	m_cs->Leave();
}

uint32_t CxDispatcher::MoveOutAsMuch(void* h, ArrayBuffer* pDstBuffer)
{
	m_cs->Enter();
	uint32_t moveCount = ArrayBuffer::MoveAsMuch(pDstBuffer, (ArrayBuffer*)h);
	m_cs->Leave();
	return(moveCount);
}
